﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Drawing;

namespace Krilib.Control.MessageObjects
{
    public class Text : MessageObject
    {
        private string _value;
        public string Value
        {
            get { return _value; }
            set
            {
                _value = value;
                Bridge.MainForm.RefreshTL(true);
            }
        }

        public Color _foreColor;
        public Color ForeColor
        {
            get { return _foreColor; }
            set
            {
                _foreColor = value;
                Bridge.MainForm.RefreshTL(false);
            }
        }

        protected virtual Font InternalFont
        {
            get { return Bridge.Config.Font; }
        }

        public Text()
            : this(String.Empty) { }

        public Text(string text)
            : this(text, Bridge.Config.ForeColor) { }

        public Text(string text, Color forecolor)
        {
            _value = text;
            _foreColor = forecolor;
        }

        protected object DrawLocker = new object();
        protected List<MeasuringBuffer> drawBuf = new List<MeasuringBuffer>();
        protected class MeasuringBuffer
        {
            public MeasuringBuffer(string value, Rectangle dest)
            {
                Value = value;
                Destination = dest;
            }
            public String Value { get; set; }
            public Rectangle Destination { get; set; }
        }

        public override void Draw(Graphics g, Point location)
        {
            lock (DrawLocker)
            {
                Rectangle bound = new Rectangle();
                foreach (var dat in drawBuf)
                {
                    bound = dat.Destination;
                    bound.X += location.X;
                    bound.Y += location.Y;
                    TextRenderer.DrawText(
                        g,
                        dat.Value,
                        InternalFont,
                        bound,
                        ForeColor,
                        TextFormatFlags.Left | TextFormatFlags.SingleLine | TextFormatFlags.Top | TextFormatFlags.NoPadding | TextFormatFlags.NoPrefix);
                }
            }
        }

        Rectangle cRect = new Rectangle();
        public override IEnumerable<Rectangle> Measure(Rectangle target, int initHeight)
        {
            cRect = target;
            IntPtr hWnd = (IntPtr)((Form)Bridge.MainForm).Invoke(new Func<IntPtr>(() => { return ((Form)Bridge.MainForm).Handle; }));
            using (Graphics g = Graphics.FromHwnd(hWnd))
            {
                if (Value == null) yield break;
                lock (DrawLocker)
                {
                    drawBuf.Clear();
                    string buf = Value.Clone() as string;
                    Size s = new Size();
                    while (buf != String.Empty)
                    {
                        s = MStr(g, buf);
                        if (s.Width < cRect.Width)
                        {
                            if (s.Height > cRect.Height)
                                cRect.Height = s.Height;
                            Rectangle curRect = new Rectangle(cRect.Location, s);
                            drawBuf.Add(new MeasuringBuffer(buf, curRect));
                            yield return curRect;
                            Measuring.HorizontalShift(ref cRect, s.Width);
                            break;
                        }
                        else
                        {
                            int drawlen = GetSuitableLength(g, 0, buf.Length, cRect.Width, buf);
                            string cur = buf.Substring(0, drawlen);
                            buf = buf.Remove(0, drawlen);
                            s = MStr(g, cur);

                            Rectangle curRect = new Rectangle(cRect.Location, s);
                            drawBuf.Add(new MeasuringBuffer(cur, curRect));
                            yield return curRect;
                            cRect.Height = curRect.Height;
                            Measuring.NewLine(ref cRect);
                        }
                    }
                    yield break;
                }
            }
        }

        protected int GetSuitableLength(Graphics g, int subidx, int diff, int targetWidth, string complete)
        {
            if (targetWidth < 5)
                return 1;
            bool overflow = diff + subidx > complete.Length;
            if (subidx == 0 && diff == 0) return 1;
            Size cSize = new Size();
            if (!overflow)
                cSize = MStr(g, complete.Substring(0, subidx + diff));
            if (cSize.Width > targetWidth)
            {
                if (diff + subidx == 1)
                    return 1;
                //too long
                if (diff == 1)
                    return subidx + diff - 1;
                return GetSuitableLength(g, subidx, diff / 2, targetWidth, complete);
            }
            else
            {
                if (overflow)
                    return complete.Length;
                //too short
                if (complete.Length == subidx) return subidx;
                return GetSuitableLength(g, subidx + diff, diff > 1 ? diff / 2 : 1 , targetWidth, complete);
            }
        }

        public override Rectangle MeasureEndRect
        {
            get { return cRect; }
        }

        public override void Reset()
        {
            lock (DrawLocker)
            {
                drawBuf.Clear();
            }
        }

        public override IEnumerable<Rectangle> ItemRect
        {
            get
            {
                return from MeasuringBuffer item in drawBuf
                       select item.Destination;
            }
        }

        /// <summary>
        /// Measure String Size
        /// </summary>
        protected Size MStr(Graphics g, string t)
        {
            return TextRenderer.MeasureText(g, t, InternalFont, new Size(), TextFormatFlags.NoPadding | TextFormatFlags.NoPrefix);
        }
    }
}
